home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_200 / 283_01 / svideo.asm < prev    next >
Encoding:
Assembly Source File  |  1988-12-15  |  29.1 KB  |  721 lines

  1.  
  2. ; SVIDEO.ASM - after Microsoft Systems Journal, Nov. 1988, p. 6
  3. ;
  4. ;               *** Caution: Small Model stack bias! ***
  5. ;
  6. ; This program handles direct writes to the IBM PC video screen buffer
  7. ; located in RAM memory.  Works with all 100% compatible clones, and
  8. ; correctly eliminates snow when using CGA.  See the article in MSJ:
  9. ; "Building a device-independent video display I/O library in Microsoft C",
  10. ; by Jeff Prosise.  Requires MASM v5.0 or MASM v5.1, per documentation.
  11. ;
  12. ; Memory model support by David C. Oshel, Ames, Iowa, Dec. 9, 1988.
  13. ; This file is essentially ASMVIDEO.ASM, modified for appropriate function
  14. ; calls and returns, and DS register management for various data objects.
  15. ;
  16. ; I've added four additional functions, all of which take a len argument,
  17. ; to Prosise's original set of three:
  18. ;                               
  19. ;                  MSJ_SetFldAttr, MSJ_DispMsgLen    12/10/88
  20. ;                  MSJ_MovScrBuf,  MSJ_MovBufScr     12/11/88
  21. ;
  22. ; Added Wozniak's pattern fill to MSJ_SetFldAttr on 12/12/88, and figured
  23. ; out why I still had snow on a CGA (NCR PC 6300) on 12/13/88.  See next.
  24. ;
  25. ;                         *** C A U T I O N ***
  26. ;
  27. ; It turns out that Prosise's XCHG AX,BX and STOSW pattern in the snow
  28. ; elimination code is a VERY time critical optimization.  I actually had
  29. ; to read the article before I could expand on the original routines.  Ouch.
  30. ;       --- 12/13/88, d.c.oshel
  31. ;
  32.  
  33.         .MODEL SMALL
  34.         .CODE
  35.         PUBLIC _MSJ_MovBufScr, _MSJ_MovScrBuf
  36.         PUBLIC _MSJ_SetFldAttr, _MSJ_DispMsgLen
  37.         PUBLIC _MSJ_DispString, _MSJ_DispCharAttr, _MSJ_GetCharAttr
  38.  
  39.  
  40. ;
  41. ; The C interface for these functions and structures looks like this:
  42. ;
  43. ;struct MSJ_VideoInfo {
  44. ;    unsigned char mode;
  45. ;    unsigned char rows;
  46. ;    unsigned char columns;
  47. ;    unsigned char ColorFlag;
  48. ;    unsigned char SnowFlag;
  49. ;    unsigned int  BufferStart;
  50. ;    unsigned int  SegAddr;
  51. ;};
  52. ;
  53. ;extern struct MSJ_VideoInfo video;  /* defined in CVIDEO.C */
  54. ;
  55. ;
  56. ;/* MSJ, Nov. 1988
  57. ;   */
  58. ;extern int cdecl MSJ_GetCharAttr( char row, char col, 
  59. ;                                  struct MSJ_VideoInfo * sptr );
  60. ;
  61. ;/* MSJ, Nov. 1988
  62. ;   */
  63. ;extern void cdecl MSJ_DispCharAttr( char ch, 
  64. ;                                    char row, char col, 
  65. ;                                    char VideoAttr, 
  66. ;                                    struct MSJ_VideoInfo * sptr );
  67. ;
  68. ;
  69. ;/* MSJ, Nov. 1988
  70. ;   */
  71. ;extern void cdecl MSJ_DispString( char * msg, 
  72. ;                                  char row, char col, 
  73. ;                                  char VideoAttr, 
  74. ;                                  struct MSJ_VideoInfo * sptr ); 
  75. ;
  76. ;
  77. ;/* MSJ_DispMsgLen is like MSJ_DispString, but takes a length argument,
  78. ;   and does not disturb screen attributes in the receiving field
  79. ;   12/10/88, d.c.oshel
  80. ;   */
  81. ;extern void cdecl MSJ_DispMsgLen( char * msg, 
  82. ;                                  char row, char col, 
  83. ;                                  int len,
  84. ;                                  struct MSJ_VideoInfo * sptr ); 
  85. ;
  86. ;
  87. ;/* MSJ_SetFldAttr takes a length argument and clears a field of the
  88. ;   screen to the given char having the given attribute
  89. ;   12/10/88, d.c.oshel
  90. ;   */
  91. ;extern void cdecl MSJ_SetFldAttr( char ch, 
  92. ;                                  char row, 
  93. ;                                  char col, 
  94. ;                                  char VideoAttr,
  95. ;                                  int len,
  96. ;                                  struct MSJ_VideoInfo * sptr );
  97. ;
  98. ;
  99. ;/* move count WORDS from screen to far buffer, 12/10/88, d.c.oshel 
  100. ;   */
  101. ;extern void cdecl MSJ_MovScrBuf(  char far * buffer,
  102. ;                                  char row,
  103. ;                                  char col,
  104. ;                                  int count,
  105. ;                                  struct MSJ_VideoInfo * sptr );
  106. ;
  107. ;
  108. ;/* move count WORDS from far buffer to screen, 12/10/88, d.c.oshel 
  109. ;   */
  110. ;extern void cdecl MSJ_MovBufScr(  char far * buffer,
  111. ;                                  char row,
  112. ;                                  char col,
  113. ;                                  int count,
  114. ;                                  struct MSJ_VideoInfo * sptr );
  115. ;
  116.  
  117.  
  118. ;-----------------------------------------------------------------------
  119. ; _MSJ_MovBufScr   copies count words from the user's far save buffer to 
  120. ;                  the active video buffer
  121. ;                  12/10/88, d.c.oshel 
  122. ;
  123. ; _MSJ_MovScrBuf   copies count words from the active video buffer to 
  124. ;                  the user's far save buffer
  125. ;                  12/10/88, d.c.oshel 
  126. ;-----------------------------------------------------------------------
  127. ;
  128. ; Arguments on the stack (SMALL MODEL):
  129. ;
  130. ;                       [bp+14]  <--- struct MSJ_VideoInfo * sptr
  131. ;                       [bp+12]  <--- int count
  132. ;                       [bp+10]  <--- char col
  133. ;                       [bp+8]   <--- char row
  134. ;                       [bp+6]   <--- seg\ char far * buffer
  135. ;                       [bp+4]   <--- ofs/ 
  136. ;
  137. _MSJ_MovBufScr  PROC
  138.                 push    bp      ;save regs & establish stack frame
  139.                 mov     bp,sp
  140.                 push    si
  141.                 push    di
  142.                 push    es
  143.                 push    ds      ;large model uses data segments anywhere
  144.  
  145. ; COUNT:        load CX with the number of bytes to move
  146. ;
  147.                 mov     cx,[bp+12]      ;CX <-- count
  148.                 cmp     cx,1            ;we're already done if CX <= 0
  149.                 jl      exitmbs
  150.  
  151. ; DESTINATION:  set ES:DI to point to screen buffer absolute address
  152. ;               note - this changes DS to video structure's segment
  153. ;
  154.                 mov     bx,[bp+14]          ;DS:BX <-- ptr to video structure
  155.                 test    byte ptr [bx+4], 1  ;is video.SnowFlag clear?
  156.                 pushf                       ;save answer for later
  157.  
  158.                 mov     dh,[bp+8]   ;DH <-- row
  159.                 mov     dl,[bp+10]  ;DL <-- col
  160.                 call    AddressOf   ;DI <-- screen offset
  161.                 mov     es,[bx+8]   ;ES <-- video.SegAddr (screen segment)
  162.                                          
  163. ; SOURCE:       set DS:SI to point to user's save buffer
  164. ;               note - this changes DS to user's far buffer segment
  165. ;
  166.                 lds     si,[bp+4]       ;DS:SI <-- ptr to source
  167.                 cld
  168.                 popf
  169.                 jnz     hardway
  170.  
  171. ; BEST CASE, move the string from DS:SI to ES:DI, with immediate writes
  172. ;
  173.                 rep     movsw           ;move all immediately
  174.                 jmp     exitmbs
  175.  
  176. hardway:        lodsw                   ;get char from buffer
  177.                 xchg    ax,bx
  178.  
  179.                 cli                     ;disable interrupts
  180.                 mov     dx,3DAh         ;DX <- ptr to Status Register
  181. st1:            in      al,dx           ;if we've arrived during a
  182.                 test    al,1            ;horizontal blanking interval,
  183.                 jnz     st1             ;must wait for the start of next!
  184. st2:            in      al,dx           ;bit one is horizontal retrace
  185.                 test    al,1            ;bit three is vertical retrace
  186.                 jz      st2             ;if nz, horizontal blanking is NOW.
  187.  
  188.                 ; Move up to 16 bits ONLY to the screen, taking advantage
  189.                 ; of the 8088's 4-byte prefetch buffer to save time (!),
  190.                 ; during the 7 microseconds of horizontal blanking;
  191.                 ; Prosise says timing is so tight we MUST use the XCHG
  192.                 ; instruction to get AX back, and nothing else will do.
  193.                 ;
  194.                 ; There are 200 horizontal retrace intervals per frame,
  195.                 ; only one vertical retrace interval per frame; i.e., 200
  196.                 ; lines on the screen, horizontal retrace is the zag
  197.                 ; following raster scan's zig, vertical retrace is the
  198.                 ; corner-to-corner diagonal path back to top of screen.
  199.                 ;
  200.                 ; For reference, 65 clock cycles are about 14 µsec. on
  201.                 ; a 4.77 mHz 8088.  See the article for details.
  202.                 ;
  203.                 ; In general, "snow" is caused by the CGA trying to access
  204.                 ; video RAM at the same time as the CPU (us!) is trying
  205.                 ; to change the screen.  CGA is ALWAYS accessing video
  206.                 ; RAM, except during horizontal or vertical retrace, or
  207.                 ; unless the port is turned off.  Prosise's discussion,
  208.                 ; en precis.  NOTE:  Not all hardware that uses CGA modes
  209.                 ; is actually a CGA!  The snow elimination algorithm is
  210.                 ; so slow (relatively speaking), it should be turned off
  211.                 ; unless actually needed to eliminate snow.  For example,
  212.                 ; my ATI Graphics Solution card emulates CGA but does not
  213.                 ; require snow elimination.
  214.                 ;
  215.                 ; XCHG AX,BX, STOSW is 4+11 clocks (ca. 3.23 microseconds),
  216.                 ; while MOVSW is 18 clocks (ca. 3.88 microseconds), according
  217.                 ; to the Intel Programmer's Pocket Reference Guide.
  218.                 ;
  219.                 xchg    ax,bx
  220.                 stosw                   ;from buffer to screen
  221.  
  222.                 sti                     ;enable interrupts
  223.                 dec     cx              ;decrement byte counter
  224.                 jcxz    exitmbs         ;all done, CX has gone to 0
  225.                 jmp     hardway         ;loop back for more
  226.  
  227. exitmbs:        pop     ds      ;restore original DS, etc.
  228.                 pop     es
  229.                 pop     di
  230.                 pop     si
  231.                 pop     bp
  232.                 ret
  233. _MSJ_MovBufScr  ENDP
  234.  
  235.  
  236. _MSJ_MovScrBuf  PROC
  237.                 push    bp      ;save regs & establish stack frame
  238.                 mov     bp,sp
  239.                 push    si
  240.                 push    di
  241.                 push    es
  242.                 push    ds      ;large model uses data segments anywhere
  243.  
  244. ; COUNT:        load CX with the number of bytes to move
  245. ;
  246.                 mov     cx,[bp+12]      ;CX <-- count
  247.                 cmp     cx,1            ;we're already done if CX <= 0
  248.                 jl      exitmsb 
  249.  
  250. ; SOURCE:       set DS:SI to point to screen buffer absolute address
  251. ;               note - this changes DS to video structure's segment
  252. ;
  253.                 mov     bx,[bp+14]          ;DS:BX <-- ptr to video structure
  254.                 test    byte ptr [bx+4], 1  ;is video.SnowFlag clear?
  255.                 pushf                       ;save answer for later
  256.  
  257.                 mov     dh,[bp+8]   ;DH <-- row
  258.                 mov     dl,[bp+10]  ;DL <-- col
  259.                 call    AddressOf   ;DI <-- screen offset
  260.                 mov     es,[bx+8]   ;ES <-- video.SegAddr (screen segment)
  261.                 push    es          ;save ES:DI, screen address
  262.                 push    di          ;will pop as DS:SI just below!
  263.                                          
  264. ; DESTINATION:  set ES:DI to point to user's save buffer
  265. ;               note - this changes ES to user's far buffer segment
  266. ;
  267.                 les     di,[bp+4]       ;ES:DI <-- ptr to destination
  268.  
  269. ;               note - this changes DS to the active video buffer
  270.                 pop     si
  271.                 pop     ds              ;DS:SI <-- ptr to source
  272.  
  273.                 cld                     ;clear direction flag
  274.                 popf                    ;was video.SnowFlag clear?
  275.                 jnz     hw              ;no, do it the hard way
  276.  
  277. ; BEST CASE, move the string from DS:SI to ES:DI, with immediate writes
  278. ;
  279.                 rep     movsw           ;all at once!
  280.                 jmp     exitmsb
  281.  
  282. ; move the string from DS:SI to ES:DI, with retrace delay
  283. ;
  284. ; we're going from SCREEN to BUFFER, so must wait for blanking interval
  285. ; before READING the char/attribute
  286.  
  287. hw:             cli                     ;disable interrupts
  288.                 mov     dx,3DAh         ;DX <- ptr to Status Register
  289. st3:            in      al,dx           ;if we've arrived during a
  290.                 test    al,1            ;horizontal blanking interval,
  291.                 jnz     st3             ;must wait for the start of next!
  292. st4:            in      al,dx           ;bit one is horizontal retrace
  293.                 test    al,1            ;bit three is vertical retrace
  294.                 jz      st4             ;if nz, horizontal blanking is NOW.
  295.  
  296.                 ; TIME CRITICAL, only have 7 µsec to read 16 bits into AX
  297.                 ;
  298.                 lodsw
  299.  
  300.                 ; But going from AX to buffer can take as long as it takes
  301.                 ;
  302.                 stosw
  303.  
  304.                 sti                     ;enable interrupts
  305.                 dec     cx              ;decrement byte counter
  306.                 jcxz    exitmsb         ;all done, CX has gone to 0
  307.                 jmp     hw              ;loop back for more
  308.  
  309. exitmsb:        pop     ds      ;restore original DS, etc.
  310.                 pop     es
  311.                 pop     di
  312.                 pop     si
  313.                 pop     bp
  314.                 ret
  315. _MSJ_MovScrBuf  ENDP
  316.  
  317.  
  318. ;------------------------------------------------------------------------
  319. ; _MSJ_SetFldAttr  writes count copies of char/attribute to video buffer
  320. ;                  12/12/88, d.c.oshel 
  321. ;------------------------------------------------------------------------
  322. ;
  323. ; Arguments on the stack (SMALL MODEL):
  324. ;
  325. ;                       [bp+14]  <--- struct MSJ_VideoInfo * sptr
  326. ;                       [bp+12]  <--- int count
  327. ;                       [bp+10]  <--- char VideoAttr
  328. ;                       [bp+8]   <--- char col
  329. ;                       [bp+6]   <--- char row
  330. ;                       [bp+4]   <--- char ch
  331. ;
  332. _MSJ_SetFldAttr PROC
  333.                 push    bp      ;save regs & establish stack frame
  334.                 mov     bp,sp
  335.                 push    si
  336.                 push    di
  337.                 push    es
  338.                 push    ds      ;large model uses data segments anywhere
  339.  
  340.                 mov     cx,[bp+12]      ;CX <-- count
  341.                 cmp     cx,1            ;we're already done if CX <= 0
  342.                 jl      exitsfa 
  343.  
  344. ; DESTINATION:  set ES:DI to point to screen buffer absolute address
  345. ;               note - this changes DS to video structure's segment
  346. ;
  347.                 mov     bx,[bp+14]      ;DS:BX <-- ptr to video structure
  348.  
  349.                 mov     dh,[bp+6]       ;DH <-- row
  350.                 mov     dl,[bp+8]       ;DL <-- col
  351.                 call    AddressOf       ;DI <-- screen offset
  352.                 mov     es,[bx+8]       ;ES <-- video.SegAddr (screen segment)
  353.  
  354. ; SOURCE:  load attribute and character
  355. ;
  356.                 mov     ah,[bp+10]      ;AH <-- attr
  357.                 mov     al,[bp+4]       ;AL <-- ch
  358.  
  359. ; move the FIRST word from AX to DESTINATION, with possible retrace delay
  360. ;
  361.                 cld                         ;clear direction flag
  362.                 test    byte ptr [bx+4], 1  ;is video.SnowFlag clear?
  363.                 jz      quick               ;yes
  364.  
  365.                 ;Rats.  Got CGA, so do it slow.  Just repeats.
  366.                 ;
  367.                 ;Although slow, this is still faster than the
  368.                 ;hardway pattern fill, which would have to wait for a
  369.                 ;horizontal blanking interval for BOTH reads and writes,
  370.                 ;since both source and destination are up in video RAM.
  371.                 ;
  372. slow:           xchg    ax,bx   ;save char/attr
  373.                 mov     dx,3DAh ;DX <- ptr to Status Register
  374.                 cli             ;disable interrupts
  375. at1:            in      al,dx   ;wait for non-retrace period
  376.                 test    al,1
  377.                 jnz     at1 
  378. at2:            in      al,dx   ;wait for next horz retrace
  379.                 test    al,1
  380.                 jz      at2     ;if ready, fall into...
  381.  
  382.                 ; TIME CRITICAL, must accomplish move in 7 µsec or less
  383.                 ;
  384.                 xchg    ax,bx
  385.                 stosw           ;di advances to next word
  386.  
  387.                 sti             ;enable interrupts
  388.                 dec     cx      ;decrement byte counter
  389.                 jcxz    exitsfa ;all done, if CX went to 0
  390.                 jmp     slow    ;loop back until done
  391.  
  392. ; Pattern Fill:  ES:DI is destination of next place to be filled
  393. ;                DS:SI is original ES:DI, one word back from current ES:DI
  394. ;                DS:[SI] now contains the pattern!
  395. ;                CX contains the count of words remaining in the pattern
  396. ;
  397. ; Example with count == 5; this is one of Steve Wozniak's old tricks:
  398. ;              s  d
  399. ;               \ |
  400. ;                .....    <- fill range of 5 places with Z's
  401. ;                Z...     <- source, after loading Z (move 4 one place right)
  402. ;                 ....    <- destination at start of move
  403. ;                 Z...    <- destination after one move is obvious
  404. ;                ZZ..     <- source after one move is subtle!
  405. ;                ZZZZZ    <- final result
  406. ;
  407. ; This works because the source modifies its own next byte by moving its
  408. ; first byte to an overlapping destination.  It's efficient because in the
  409. ; best case, we can move the entire range-1 using REP MOVSW.
  410. ;
  411. quick:          push    es      ;save starting destination, ES:DI
  412.                 push    di
  413.                 stosw           ;load first word into pattern, DI advances
  414.                 pop     si      ;pop starting destination as source, DS:SI!
  415.                 pop     ds
  416.                 dec     cx      ;decrement byte counter
  417.                 jcxz    exitsfa ;all done, if CX went to 0
  418.  
  419.                 rep     movsw   ;magic pattern fill ... !
  420.  
  421. exitsfa:        pop     ds      ;restore original DS, etc.
  422.                 pop     es
  423.                 pop     di
  424.                 pop     si
  425.                 pop     bp
  426.                 ret
  427. _MSJ_SetFldAttr ENDP
  428.  
  429.  
  430. ;-----------------------------------------------------------------------
  431. ; _MSJ_DispMsgLen  writes len bytes from msg to the active video buffer
  432. ;                  without disturbing the receiving attribute field
  433. ;                  12/9/88, d.c.oshel 
  434. ;-----------------------------------------------------------------------
  435. ;
  436. ; Arguments on the stack (SMALL MODEL):
  437. ;
  438. ;                       [bp+12]  <--- struct MSJ_VideoInfo * sptr
  439. ;                       [bp+10]  <--- int len
  440. ;                       [bp+8]   <--- char col
  441. ;                       [bp+6]   <--- char row
  442. ;                       [bp+4]   <--- char * msg
  443. ;
  444. _MSJ_DispMsgLen PROC
  445.                 push    bp      ;save regs & establish stack frame
  446.                 mov     bp,sp
  447.                 push    si
  448.                 push    di
  449.                 push    es
  450.                 push    ds      ;large model uses data segments anywhere
  451.  
  452.                 mov     cx,[bp+10]      ;CX <-- length of string
  453.                 cmp     cx,1            ;we're already done if CX <= 0
  454.                 jl      dexit
  455.  
  456. ; DESTINATION:  set ES:DI to point to screen buffer absolute address
  457. ;               note - this changes DS to video structure's segment
  458. ;
  459.                 mov     bx,[bp+12]      ;DS:BX <-- ptr to video structure
  460.                 test    byte ptr [bx+4], 1  ;is video.SnowFlag clear?
  461.                 pushf                       ;save result, will jz later
  462.                 mov     dh,[bp+6]       ;DH <-- row
  463.                 mov     dl,[bp+8]       ;DL <-- col
  464.                 call    AddressOf       ;DI <-- screen offset
  465.                 mov     es,[bx+8]       ;ES <-- video.SegAddr (screen segment)
  466.  
  467. ; SOURCE:  set DS:SI to point to null-terminated ASCII string, load attribute
  468. ;          note - this changes DS to msg's segment, old value not needed
  469. ;
  470.                 mov     si,[bp+4]       ;DS:SI <-- ptr to msg
  471.                 cld                     ;clear direction flag
  472.                 popf                    ;was video.SnowFlag set?
  473.                 jnz     dmsg1           ;yes, do it the hard way
  474.  
  475.  
  476. ; move the string from SOURCE to DESTINATION, with immediate writes
  477. ;
  478. dmsg2:          movsb                   ;move character from string to screen
  479.                 inc     di              ;  and skip over attribute on screen
  480.                 dec     cx              ;decrement byte counter
  481.                 jcxz    dexit           ;all done, CX has gone to 0
  482.                 jmp     dmsg2           ;loop back for more
  483.  
  484. ; move the string from SOURCE to DESTINATION, with retrace delay
  485. ;
  486. dmsg1:          lodsb                   ;move character from string to AL
  487.                 xchg    ax,bx
  488.                 cli
  489.                 mov     dx,3DAh         ;DX <- ptr to Status Register
  490. wt1:            in      al,dx           ;wait for non-retrace period
  491.                 test    al,1
  492.                 jnz     wt1 
  493. wt2:            in      al,dx           ;wait for next horz retrace
  494.                 test    al,1
  495.                 jz      wt2             ;if ready, fall into...
  496.  
  497.                 ; TIME CRITICAL, must accomplish move in 7 µsec or less!
  498.                 ;
  499.                 xchg    ax,bx
  500.                 stosb                   ;move character from AL to screen
  501.                 inc     di              ;  and skip over attribute on screen
  502.                 sti                     ;enable interrupts
  503.  
  504.                 dec     cx              ;decrement byte counter
  505.                 jcxz    dexit           ;all done, CX has gone to 0
  506.                 jmp     dmsg1           ;loop back for more
  507.  
  508. dexit:          pop     ds      ;restore original DS, etc.
  509.                 pop     es
  510.                 pop     di
  511.                 pop     si
  512.                 pop     bp
  513.                 ret
  514.  
  515. _MSJ_DispMsgLen ENDP
  516.  
  517.  
  518. ;------------------------------------------------------------
  519. ; _MSJ_DispString writes a string to the active video buffer
  520. ;------------------------------------------------------------
  521. ;
  522. ; Arguments on the stack (SMALL MODEL):
  523. ;
  524. ;                       [bp+12]  <--- struct MSJ_VideoInfo * sptr
  525. ;                       [bp+10]  <--- char VideoAttr
  526. ;                       [bp+8]   <--- char col
  527. ;                       [bp+6]   <--- char row
  528. ;                       [bp+4]   <--- char * msg
  529. ;
  530. _MSJ_DispString PROC
  531.                 push    bp      ;save regs & establish stack frame
  532.                 mov     bp,sp
  533.                 push    si
  534.                 push    di
  535.                 push    ds
  536.                 push    es
  537.  
  538.                 mov     bx,[bp+12]      ;DS:BX <-- ptr to video structure
  539.  
  540.                 mov     si,[bp+4]       ;DS:SI <-- ptr to msg
  541.                 mov     dh,[bp+6]       ;DH <-- row
  542.                 mov     dl,[bp+8]       ;DL <-- col
  543.                 call    AddressOf       ;DI <-- screen offset
  544.                 mov     es,[bx+8]       ;ES <-- video.SegAddr (screen segment)
  545.                 mov     ah,[bp+10]      ;AH <-- VideoAttr
  546.                 cld
  547.  
  548. dstr1:          lodsb
  549.                 or      al,al
  550.                 jz      dstr3
  551.                 test    byte ptr [bx+4], 1  ;is video.SnowFlag clear?
  552.                 jz      dstr2
  553.                 call    WriteWithWait
  554.                 jmp     dstr1
  555. dstr2:          stosw
  556.                 jmp     dstr1           ;found '\0', ASCIIZ string terminator
  557.  
  558. dstr3:          pop     es
  559.                 pop     ds
  560.                 pop     di
  561.                 pop     si
  562.                 pop     bp
  563.                 ret
  564. _MSJ_DispString     ENDP
  565.  
  566.  
  567.  
  568.  
  569. ;-----------------------------------------------------------------------
  570. ; _MSJ_DispCharAttr     writes a character and attribute to the active
  571. ;                       video buffer
  572. ;-----------------------------------------------------------------------
  573. ;
  574. ; Arguments on the stack (SMALL MODEL):
  575. ;
  576. ;                       [bp+12]  <--- struct MSJ_VideoInfo * sptr
  577. ;                       [bp+10]  <--- char VideoAttr
  578. ;                       [bp+8]   <--- char col
  579. ;                       [bp+6]   <--- char row
  580. ;                       [bp+4]   <--- char ch
  581. ;
  582. _MSJ_DispCharAttr PROC
  583.                 push    bp              ;save regs & establish stack frame
  584.                 mov     bp,sp
  585.                 push    di
  586.                 push    si
  587.                 push    ds
  588.                 push    es
  589.                 mov     bx,[bp+12]      ;DS:BX <-- ptr to video structure
  590.  
  591.                 mov     dh,[bp+6]       ;DH <-- row
  592.                 mov     dl,[bp+8]       ;DL <-- col
  593.                 call    AddressOf       ;DI <-- screen offset
  594.                 mov     es,[bx+8]       ;ES <-- video.SegAddr (screen segment)
  595.                 mov     al,[bp+4]          ;AL <-- ch
  596.                 mov     ah,[bp+10]         ;AH <-- VideoAttr
  597.  
  598.                 test    byte ptr [bx+4], 1 ;is DS:video.SnowFlag clear?
  599.                 jz      dca1               ;yes, write direct
  600.                 call    WriteWithWait
  601.                 jmp     short dca2
  602. dca1:           stosw                   ;ES:[DI] <-- AX  a screen write
  603.  
  604. dca2:           pop     es
  605.                 pop     ds
  606.                 pop     si
  607.                 pop     di
  608.                 pop     bp
  609.                 ret
  610. _MSJ_DispCharAttr   ENDP
  611.  
  612.  
  613.  
  614. ;---------------------------------------------------------------
  615. ; _MSJ_GetCharAttr  returns the character and attribute at the
  616. ;                   specified row & column; i.e.,
  617. ;                   On Exit: AH <-- attribute, AL <-- character
  618. ;---------------------------------------------------------------
  619. ;
  620. ; Arguments on the stack (SMALL MODEL):
  621. ;
  622. ;                       [bp+8]   <--- struct MSJ_VideoInfo * sptr
  623. ;                       [bp+6]   <--- char col
  624. ;                       [bp+4]   <--- char row
  625. ;
  626. _MSJ_GetCharAttr PROC
  627.                 push    bp              ;save regs & establish stack frame
  628.                 mov     bp,sp
  629.                 push    di
  630.                 push    si
  631.                 push    ds
  632.                 push    es
  633.                 mov     bx,[bp+8]       ;DS:BX <-- ptr to video structure
  634.                 test    byte ptr [bx+4],1  ;is video.SnowFlag clear?
  635.                 pushf
  636.  
  637.                 mov     dh,[bp+4]       ;DH <-- row
  638.                 mov     dl,[bp+6]       ;DL <-- col
  639.                 call    AddressOf       ;DI <-- screen offset
  640.                 mov     es,[bx+8]       ;ES <-- video.SegAddr (screen segment)
  641.                 push    es
  642.                 pop     ds
  643.                 push    di
  644.                 pop     si
  645.                 cld
  646.                 popf
  647.                 jz      gca3
  648.  
  649.                 mov     dx,3DAh   ;DX <- PORT ptr to CRTC status register
  650.                 cli
  651. gca1:           in      al,dx     ;wait for non-retrace period
  652.                 test    al,1
  653.                 jnz     gca1
  654. gca2:           in      al,dx     ;wait for next horizontal retrace
  655.                 test    al,1
  656.                 jz      gca2      ;if ready, fall into...
  657.  
  658. gca3:           lodsw             ;AH <- attr, AL <- char
  659.                 sti
  660.                 pop     es
  661.                 pop     ds
  662.                 pop     si
  663.                 pop     di
  664.                 pop     bp
  665.                 ret
  666. _MSJ_GetCharAttr    ENDP
  667.  
  668.  
  669. ;-----------------------------------------------------------------------------
  670. ; AddressOf     returns the video buffer address that
  671. ;               corresponds to the specified row & column
  672. ;
  673. ;       Entry:  DS:BX  -  address of VideoInfo structure
  674. ;               DH,DL  -  row, column
  675. ;       Exit:   DI     -  offset address
  676. ;-----------------------------------------------------------------------------
  677. AddressOf       PROC NEAR
  678.                 mov     al,[bx+2]       ;columns per row
  679.                 mul     dh              ;multiply by row number
  680.                 xor     dh,dh           ;add column number
  681.                 add     ax,dx
  682.                 shl     ax,1            ;mul by 2
  683.                 add     ax,[bx+6]       ;add offset of active page
  684.                 mov     di,ax           ;DI <- result
  685.                 ret
  686. AddressOf       ENDP
  687.  
  688.  
  689. ;----------------------------------------------------------------
  690. ; WriteWithWait writes a character and attribute to a CGA during
  691. ;               the horizontal blanking interval; this is the
  692. ;               Microsoft-approved *** SNOWBUSTER *** routine!
  693. ;
  694. ;       Entry:  ES:DI  -  video buffer address
  695. ;               AH,AL  -  character, attribute
  696. ;       Exit:   CX and DX are trashed
  697. ;----------------------------------------------------------------
  698. WriteWithWait  PROC NEAR
  699.                mov     bx,ax           ;save char/attr
  700.                mov     dx,3DAh         ;DX <- ptr to Status Register
  701.                cli                     ;disable interrupts
  702. www1:          in      al,dx           ;wait for non-retrace period
  703.                test    al,1
  704.                jnz     www1
  705. www2:          in      al,dx           ;wait for next horz retrace
  706.                test    al,1
  707.                jz      www2
  708.  
  709.                ; TIME CRITICAL, must accomplish move in 7 µsec or less!
  710.                ; MOV AX,CX will *NOT* do the job!  Must be XCHG.
  711.                ;
  712.                xchg    ax,bx           ;retrieve char/attr
  713.                stosw                   ;write to video memory at ES:[DI]
  714.                sti                     ;enable interrupts
  715.                ret
  716. WriteWithWait  ENDP
  717.  
  718.                 END
  719.  
  720.  
  721.